﻿using System;
using UnityEngine;

public sealed class Maths
{
    private static volatile Maths instance;
    private static object syncRoot = new object();

    private Maths() { }

    //---------------------------------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------------------
    public static Maths Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                        instance = new Maths();
                }
            }

            return instance;
        }
    }

    //---------------------------------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------------------
    public static Vector2 Vec2LinearBlend(ref Vector2 _vCurrent, ref Vector2 _vTarget, float _fDisplacement)
    {
        if(_vCurrent == _vTarget){
            return _vCurrent;
        }
        Vector2 vDirection  =   _vTarget - _vCurrent;
        float fDistance     =   vDirection.magnitude;
        vDirection          *=  _fDisplacement / fDistance;

        // get the new candidate
        Vector2 vCandidate =    _vCurrent + vDirection;

        // Check if we have surpassed the target vector
        if (Vector2.Dot(vDirection, _vCurrent  - vCandidate) < 0) 
        {
            return _vTarget;
        }

        return vCandidate;        
    }

    //---------------------------------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------------------
    public static Vector2 Vec2LinearReduction(ref Vector2 _vCurrent, float _fDisplacement)
    {
        if (_vCurrent == Vector2.zero)
        {
            return _vCurrent;
        }

        // get the new candidate
        Vector2 vCandidate =    _vCurrent * (1 - _fDisplacement / _vCurrent.magnitude);

        // Check if we have surpassed the target vector
        if (Vector2.Dot(vCandidate, _vCurrent) < 0)
        {
            return Vector2.zero;
        }
        return vCandidate;
    }
	
    //---------------------------------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------------------
	public static Vector2 Vec2Blend(ref Vector2 _vOrigin, ref Vector2 _vEnd, float _fBlendAmmount)
	{
		return _vEnd * _fBlendAmmount + _vOrigin * (1 - _fBlendAmmount);
	}
	
	//---------------------------------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------------------
	public static Vector3 Vec3Blend(ref Vector3 _vOrigin, ref Vector3 _vEnd, float _fBlendAmmount)
	{
		return _vEnd * _fBlendAmmount + _vOrigin * (1 - _fBlendAmmount);
	}
	
    //---------------------------------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------------------
    public static float FloatLinearBlend(float _fCurValue, float _fTargetValue, float _BlendAmmount)
    {
        if (_fCurValue < _fTargetValue)
        {
            return Mathf.Min(_fCurValue + _BlendAmmount, _fTargetValue);
        }
        else if (_fCurValue > _fTargetValue)
        {
            return Mathf.Max(_fCurValue - _BlendAmmount, _fTargetValue);
        }
        return _fTargetValue;
    }

    //---------------------------------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------------------
    public static float FloatLinearReduction(float _fCurValue, float _BlendAmmount)
    {
        if (_fCurValue < 0)
        {
            return Mathf.Min(_fCurValue + _BlendAmmount, 0.0f);
        }
        else if (_fCurValue > 0)
        {
            return Mathf.Max(_fCurValue - _BlendAmmount, 0.0f);
        }
        // Unreachable code
        return 0.0f;
    }
	
    //---------------------------------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------------------
	public static float FloatBlend(float _fOrigin, float _fEnd, float _fBlendAmmount)
	{
		return _fEnd * _fBlendAmmount + _fOrigin * (1 - _fBlendAmmount);
	}
	
    //---------------------------------------------------------------------------------------------------------
    //---------------------------------------------------------------------------------------------------------
	public static float FloatMap(float _fCurOrigin, float _fCurEnd, float _fDesiredOrigin, float _fDesiredEnd, float _fCurVal)
	{
		return 	_fDesiredOrigin + (_fCurVal - _fCurOrigin)/(_fCurEnd - _fCurOrigin) * (_fDesiredEnd - _fDesiredOrigin);
	}
}